/**
 * @PROJECT
 * @DATE 2017年12月27日 下午10:15:59
 * @AUTHOR LUYU
 */
package com.kool.kadmin.common.util;

import com.alibaba.druid.filter.config.ConfigTools;
import com.kool.kadmin.common.constant.ErrorCode;
import com.kool.kadmin.common.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * @DESCRIBE 安全工具
 * @AUTHOR LUYU
 * @DATE 2017年12月27日 下午10:15:59
 */
@Slf4j
public class SecurityUtil {
    // 加密算法
    public static final String SECURITY_METHOD_MD5 = "md5";
    public static final String SECURITY_METHOD_SHA = "sha-256";
    public static final String SECURITY_METHOD_AES = "AES";

    /**
     * @param sContent
     * @return
     * @DESCRIBE
     * @DATE 2017年12月27日 下午10:16:59
     */
    public static String sha(String sContent) {
        return securityMethod(sContent, SECURITY_METHOD_SHA);
    }

    /**
     * @param sContent
     * @return
     * @DESCRIBE
     * @DATE 2017年12月27日 下午10:21:42
     */
    public static String md5(String sContent) {
        return securityMethod(sContent, SECURITY_METHOD_MD5);
    }

    private static String securityMethod(String sContent, String method) {
        String encryptText = null;
        try {
            MessageDigest m = MessageDigest.getInstance(method);
            m.update(sContent.getBytes("UTF8"));
            byte s[] = m.digest();
            encryptText = hex(s);
        } catch (NoSuchAlgorithmException e) {
            String errUid = StringUtil.getUUID();
            log.error(errUid, e);
            // 系统异常，请联系管理员！异常码：%s
            throw new AppException(ErrorCode.SYSTEM_ERROR.getCode(), e, errUid);
        } catch (UnsupportedEncodingException e) {
            String errUid = StringUtil.getUUID();
            log.error(errUid, e);
            // 系统异常，请联系管理员！异常码：%s
            throw new AppException(ErrorCode.SYSTEM_ERROR.getCode(), e, errUid);
        }
        return encryptText;

    }

    /**
     * @param arr
     * @return
     * @DESCRIBE
     * @DATE 2017年12月27日 下午10:27:26
     */
    private static String hex(byte[] arr) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < arr.length; ++i) {
            sb.append(Integer.toHexString((arr[i] & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString();
    }

    /**
     * @param text
     * @return
     * @DESCRIBE base64编码
     * @DATE 2017年12月31日 下午3:12:08
     */
    public static String base64Encode(String text) {
        String encodeText = null;
        try {
            encodeText = Base64.encodeBase64String(text.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            String errUid = StringUtil.getUUID();
            log.error(errUid, e);
            // 系统异常，请联系管理员！异常码：%s
            throw new AppException(ErrorCode.SYSTEM_ERROR.getCode(), e, errUid);
        }
        return encodeText;
    }

    /**
     * @param encodeText
     * @return
     * @DESCRIBE base64解码
     * @DATE 2017年12月31日 下午3:12:22
     */
    public static String base64Decode(String encodeText) {
        String decodeText = new String(Base64.decodeBase64(encodeText));
        return decodeText;
    }

    /**
     * @return
     * @DESCRIBE 获取RSA密钥对
     * @DATE 2018年7月16日 下午3:43:54
     */
    public static KeyPair generateRSAKey() {
        KeyPair kp = null;
        try {
            SecureRandom secureRandom = new SecureRandom();
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
            kpg.initialize(1024, secureRandom);
            kp = kpg.genKeyPair();
        } catch (NoSuchAlgorithmException e) {
            String errUid = StringUtil.getUUID();
            log.error(errUid, e);
            // 系统异常，请联系管理员！异常码：%s
            throw new AppException(ErrorCode.SYSTEM_ERROR.getCode(), e, errUid);
        }
        return kp;
    }

    /**
     * @param toDir
     * @DESCRIBE 生成密钥对文件到指定路径
     * @DATE 2018年7月16日 下午4:47:09
     */
    public static void generateRSAKeyFile(String toDir) {
        KeyPair kp = generateRSAKey();
        PublicKey pbKey = kp.getPublic();
        PrivateKey prKey = kp.getPrivate();
        byte[] pbByte = pbKey.getEncoded();
        byte[] prByte = prKey.getEncoded();
        String pbString = Base64.encodeBase64String(pbByte);
        String prString = Base64.encodeBase64String(prByte);

        if (StringUtil.isEmpty(toDir)) {
            throw new RuntimeException("toDir is null!");
        }
        FileUtil.writeFile(pbString, new StringBuilder().append(toDir).append(FileUtil.getSeparator()).append("public.key").toString(), "UTF-8", false);
        FileUtil.writeFile(prString, new StringBuilder().append(toDir).append(FileUtil.getSeparator()).append("private.key").toString(), "UTF-8", false);
    }

    /**
     * @param enContent
     * @param pbKey
     * @return
     * @DESCRIBE RSA加密
     * @DATE 2018年7月16日 下午4:51:17
     */
    public static byte[] encryptRSA(byte[] enContent, PublicKey pbKey) {
        // 获取公钥及参数e,n
        RSAPublicKey pbk = (RSAPublicKey) pbKey;
        BigInteger e = pbk.getPublicExponent();
        BigInteger n = pbk.getModulus();
        // 获取明文m
        BigInteger m = new BigInteger(enContent);
        // 计算密文c
        BigInteger c = m.modPow(e, n);
        return c.toByteArray();
    }

    /**
     * @param deContent
     * @param prKey
     * @return
     * @DESCRIBE RSA解密
     * @DATE 2018年7月16日 下午4:51:31
     */
    public static byte[] decryptRSA(byte[] deContent, PrivateKey prKey) {
        // 读取密文
        BigInteger c = new BigInteger(deContent);
        // 读取私钥
        RSAPrivateKey prk = (RSAPrivateKey) prKey;
        BigInteger d = prk.getPrivateExponent();

        // 获取私钥参数及解密
        BigInteger n = prk.getModulus();
        BigInteger m = c.modPow(d, n);

        // 显示解密结果
        byte[] mt = m.toByteArray();
        return mt;
    }

    /**
     * @param keyBytes 未经过base64编码的RSA公钥
     * @return
     * @DESCRIBE 还原公钥
     * @DATE 2018年7月16日 下午4:05:19
     */
    public static PublicKey getPublicKey(byte[] keyBytes) {
        PublicKey publicKey = null;
        try {
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            publicKey = keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            String errUid = StringUtil.getUUID();
            log.error(errUid, e);
            // 系统异常，请联系管理员！异常码：%s
            throw new AppException(ErrorCode.SYSTEM_ERROR.getCode(), errUid);
        } catch (InvalidKeySpecException e) {
            String errUid = StringUtil.getUUID();
            log.error(errUid, e);
            // 系统异常，请联系管理员！异常码：%s
            throw new AppException(ErrorCode.SYSTEM_ERROR.getCode(), errUid);
        }
        return publicKey;
    }

    /**
     * @param key 经过base64编码的RSA公钥
     * @return
     * @DESCRIBE 还原公钥
     * @DATE 2018年7月16日 下午5:03:16
     */
    public static PublicKey getPublicKey(String key) {
        return getPublicKey(Base64.decodeBase64(key));
    }

    /**
     * @param keyBytes 未经过base64编码的RSA私钥
     * @return
     * @DESCRIBE 还原私钥
     * @DATE 2018年7月16日 下午4:09:03
     */
    public static PrivateKey getPrivateKey(byte[] keyBytes) {
        PrivateKey privateKey = null;
        try {
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            privateKey = keyFactory.generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException e) {
            String errUid = StringUtil.getUUID();
            log.error(errUid, e);
            // 系统异常，请联系管理员！异常码：%s
            throw new AppException(ErrorCode.SYSTEM_ERROR.getCode(), errUid);
        } catch (InvalidKeySpecException e) {
            String errUid = StringUtil.getUUID();
            log.error(errUid, e);
            // 系统异常，请联系管理员！异常码：%s
            throw new AppException(ErrorCode.SYSTEM_ERROR.getCode(), errUid);
        }
        return privateKey;
    }

    /**
     * @param key 经过base64编码的RSA私钥
     * @return
     * @throws AppException
     * @DESCRIBE 还原私钥
     * @DATE 2018年7月16日 下午5:03:49
     */
    public static PrivateKey getPrivateKey(String key) {
        return getPrivateKey(Base64.decodeBase64(key));
    }

    /**
     * @param pwd
     * @DESCRIBE druid 生成密钥对和密文
     * @DATE 2018年9月13日 下午10:03:49
     */
    public static String druidGenKeyPair(String pwd) {
        try {
            String[] arr = ConfigTools.genKeyPair(512);
            System.out.println("privateKey:" + arr[0]);
            System.out.println("publicKey:" + arr[1]);
            String epwd = ConfigTools.encrypt(arr[0], pwd);
            return epwd;
        } catch (Exception e) {
            String errUid = StringUtil.getUUID();
            log.error(errUid, e);
            // 系统异常，请联系管理员！异常码：%s
            throw new AppException(ErrorCode.SYSTEM_ERROR.getCode(), errUid);
        }

    }

    /**
     * @param publicKey
     * @param pwd
     * @DESCRIBE druid 解密
     * @DATE 2018年9月13日 下午10:03:49
     */
    public static String druidDecryptPWD(String publicKey, String pwd) {
        try {
            String dpwd = ConfigTools.decrypt(publicKey, pwd);
            return dpwd;
        } catch (Exception e) {
            String errUid = StringUtil.getUUID();
            log.error(errUid, e);
            // 系统异常，请联系管理员！异常码：%s
            throw new AppException(ErrorCode.SYSTEM_ERROR.getCode(), errUid);

        }
    }

    /**
     * 多次sha加密
     *
     * @param pwd
     * @param N
     * @return
     */
    public static String sha(String pwd, int N) {
        String tmp = pwd;
        for (int i = 0; i < N; i++) {
            tmp = sha(tmp);
        }
        return tmp;
    }

    /**
     * 生成AES秘钥
     *
     * @return
     */
    public static String generateAESKey() {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(SECURITY_METHOD_AES);
            keyGenerator.init(128);
            //产生原始对称密钥
            SecretKey secretKey = keyGenerator.generateKey();
            //获得原始对称密钥的字节数组
            byte[] keyBytes = secretKey.getEncoded();
            return Base64.encodeBase64String(keyBytes);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * AES加密
     *
     * @param content
     * @param key
     * @return
     */
    private static byte[] encryptAES(byte[] content, Key key) {
        try {
            Cipher cipher = Cipher.getInstance(SECURITY_METHOD_AES);
            cipher.init(cipher.ENCRYPT_MODE, key);
            //将加密并编码后的内容解码成字节数组
            byte[] result = cipher.doFinal(content);

            return result;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * AES加密
     *
     * @param content
     * @param base64Key
     * @return
     */
    public static String encryptAES(String content, String base64Key) {
        Key key = new SecretKeySpec(Base64.decodeBase64(base64Key), SECURITY_METHOD_AES);
        return Base64.encodeBase64String(encryptAES(content.getBytes(), key));
    }

    /**
     * AES解密
     *
     * @param content
     * @param key
     * @return
     */
    private static byte[] decryptAES(byte[] content, Key key) {

        try {
            Cipher cipher = Cipher.getInstance(SECURITY_METHOD_AES);
            //初始化密码器，第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作，第二个参数为使用的KEY
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] result = cipher.doFinal(content);
            return result;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * AES解密
     *
     * @param decryptedContent
     * @param base64Key
     * @return
     */
    public static String decryptAES(String decryptedContent, String base64Key) {
        Key key = new SecretKeySpec(Base64.decodeBase64(base64Key), SECURITY_METHOD_AES);
        return new String(decryptAES(Base64.decodeBase64(decryptedContent), key));
    }

    public static void main(String[] args) {
//		String pwd = "ly";
        // System.out.println(md5(pwd));
        // System.out.println(md5(pwd).length());
        // System.out.println(sha(pwd));
        // System.out.println(sha(pwd).length());
        // long beg = System.currentTimeMillis();
        // for (int i = 0; i < 10000; i++) {
        // pwd = sha(pwd);
        // }
        // long end = System.currentTimeMillis();
        // System.out.println("cost:" + String.valueOf(end - beg));
        // System.out.println(pwd);

        // System.out.println(base64Encode(pwd));
        // System.out.println(base64Decode(base64Encode(pwd)));

        // pwd = pwd + "|8c203";
        // for (int i = 0; i < 1; i++) {
        // pwd = sha(pwd);
        // }
        // System.out.println(pwd);

//        KeyPair kp = generateRSAKey();
//        PublicKey pbKey = kp.getPublic();
//        PrivateKey prKey = kp.getPrivate();
//        byte[] pbByte = pbKey.getEncoded();
//        byte[] prByte = prKey.getEncoded();
//        String pbString = Base64.encodeBase64String(pbByte);
//        String prString = Base64.encodeBase64String(prByte);
//        System.out.println("publicKey:" + pbString);
//        System.out.println("privateKey:" + prString);
//
        String pkg = "123明文！@#}";
//
//        PublicKey pbk = getPublicKey(Base64.decodeBase64(pbString));
//        PrivateKey prk = getPrivateKey(Base64.decodeBase64(prString));
//        byte[] enContent = encryptRSA(pkg.getBytes(), pbk);
//        String ePkg = Base64.encodeBase64String(enContent);
//        System.out.println(ePkg);
//
//        byte[] deContent = decryptRSA(Base64.decodeBase64(ePkg), prk);
//
//        System.out.println(new String(deContent));


//		druidGenKeyPair("zaq1XSW@");

//		druidDecryptPWD("MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMODI7iOm6QfGEa+0/SYSSBw+rjUzPJAVFjijkyC12ozlj03klfRkkivWC44dgmhon6d9cNt7n63ok50ezhj4V0CAwEAAQ==", "wHjIGDEWf4r9sJrwym7Mli+bMlpJT2Bp4mzpAhLmcrI5AF26EICfBj5YaPJwsnGIERzi6kahIn42PjcTga/qfw==");

//        String pwd = base64Encode(sha("luyu@123|6da7a", 2));
//        String pwd2 = base64Encode(sha("luyu@123|6da7a", 2));
//        log.debug("[{}]", pwd);
//        log.debug("[{}]", pwd2);
//
//        String oldPwd = base64Encode("luyu@123");
//        log.debug("[{}]", oldPwd);

        String keyString = generateAESKey();
        log.debug("keyString = {}", keyString);

        String encryptedPkg = encryptAES(pkg, keyString);
        log.debug("encryptedPkg = {}", encryptedPkg);

        String decryptedPkg = decryptAES(encryptedPkg, keyString);
        log.debug("decryptedPkg = {}", decryptedPkg);
    }
}
